package com.cardone.platform.common.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.Setter;
import net.coobird.thumbnailator.Thumbnails;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.cardone.common.Characters;
import com.cardone.context.ContextHolder;
import com.cardone.context.action.Run2Action;
import com.cardone.platform.common.dto.EntityFileDto;
import com.cardone.platform.common.service.EntityFileService;
import com.cardone.platform.configuration.dto.DictionaryDto;
import com.cardone.platform.configuration.service.DictionaryService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mongodb.gridfs.GridFSDBFile;

/**
 * 文件数据工具类
 *
 * @author yaohaitao
 *
 */
public class EntityFileUtils {
	/**
	 * 生成缩略图
	 *
	 * @param entityFileList
	 *          保存实体集合
	 * @param saveEntityFile
	 *          保存实体文件
	 * @param multipartFile
	 *          multipartFile
	 *
	 * @throws IOException
	 */
	private static void buildThumbnails(final List<EntityFileDto> entityFileList, final EntityFileDto saveEntityFile, final MultipartFile multipartFile) throws IOException {
		if (StringUtils.isBlank(saveEntityFile.getThumbnailsTypeCode())) {
			return;
		}

		final String imgages = ContextHolder.getBean(DictionaryService.class).readValueByTypeCodeAndCode("jpg,png,gif", "file_type", "image");

		if (!StringUtils.contains(imgages, saveEntityFile.getExtension())) {
			return;
		}

		final List<String> whList = EntityFileUtils.findListWhList(saveEntityFile);

		if (CollectionUtils.isEmpty(whList)) {
			return;
		}

		for (final String wh : whList) {
			final String[] whs = StringUtils.split(wh, Characters.meter.stringValue());

			if (ArrayUtils.getLength(whs) < 1) {
				continue;
			}

			final int width = NumberUtils.toInt(whs[0], 160);

			final int height = NumberUtils.toInt(whs[1], 160);

			final String newFilename = saveEntityFile.getFilename() + Characters.underline.stringValue() + width + Characters.underline.stringValue() + height + Characters.dot.stringValue() + saveEntityFile.getExtension();

			final EntityFileDto saveThumbnailsEntityFile = EntityFileDto.newEntityFile();

			BeanUtils.copyProperties(saveEntityFile, saveThumbnailsEntityFile);

			saveThumbnailsEntityFile.setFilename(newFilename);

			final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

			Thumbnails.of(multipartFile.getInputStream()).size(width, height).toOutputStream(outputStream);

			final InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());

			EntityFileUtils.saveFile(inputStream, saveEntityFile.getFilename());

			IOUtils.closeQuietly(inputStream);

			IOUtils.closeQuietly(outputStream);

			entityFileList.add(ContextHolder.getBean(EntityFileService.class).saveByIdOrCode(EntityFileDto.class, saveThumbnailsEntityFile));
		}
	}

	/**
	 * 查询宽高集合
	 *
	 * @param saveEntityFile
	 *          实体文件对象
	 * @return 宽高集合
	 */
	private static List<String> findListWhList(final EntityFileDto saveEntityFile) {
		final List<String> whList = Lists.newArrayList();

		if (StringUtils.isNotBlank(saveEntityFile.getThumbnailsCode())) {
			final String wh = ContextHolder.getBean(DictionaryService.class).readValueByTypeCodeAndCode(saveEntityFile.getThumbnailsTypeCode(), saveEntityFile.getThumbnailsCode());

			if (StringUtils.isBlank(wh)) {
				return whList;
			}

			whList.add(wh);

			return whList;
		}

		final List<DictionaryDto> thumbnailsList = ContextHolder.getBean(DictionaryService.class).findListByTypeCode(DictionaryDto.class, saveEntityFile.getThumbnailsTypeCode());

		if (CollectionUtils.isEmpty(thumbnailsList)) {
			return whList;
		}

		for (final DictionaryDto thumbnails : thumbnailsList) {
			whList.add(thumbnails.getValue());
		}

		return whList;
	}

	/**
	 * 查询文件
	 *
	 * @param filename
	 *          文件名
	 * @return GridFSDBFile
	 */
	private static GridFSDBFile findOneGridFSDBFile(final String filename) {
		final GridFSDBFile gridFSDBFile = ContextHolder.getBean(GridFsTemplate.class).findOne(new Query(Criteria.where("filename").is(filename)));

		return gridFSDBFile;
	}

	/**
	 * 保存文件
	 *
	 * @param inputStream
	 *          多文件
	 * @param filename
	 * @throws IOException
	 */
	private static void saveFile(final InputStream inputStream, final String filename) throws IOException {
		ContextHolder.getBean(GridFsTemplate.class).store(inputStream, filename).save();
	}

	/**
	 * 上传
	 *
	 * @param request
	 *          request
	 * @param uploadEntityFile
	 *          上传实体文件
	 * @throws Exception
	 */
	public static List<EntityFileDto> upload(final HttpServletRequest request, final EntityFileDto uploadEntityFile) throws Exception {
		final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

		List<MultipartFile> multipartFiles;

		// 没有指定名称时，取所有上传文件
		if (StringUtils.isBlank(uploadEntityFile.getName())) {
			multipartFiles = Lists.newArrayList(multipartRequest.getFileMap().values());
		} else {
			multipartFiles = multipartRequest.getFiles(uploadEntityFile.getName());
		}

		if (CollectionUtils.isEmpty(multipartFiles)) {
			return Lists.newArrayList();
		}

		final List<EntityFileDto> entityFileList = Lists.newArrayList();

		for (final MultipartFile multipartFile : multipartFiles) {
			final EntityFileDto saveEntityFile = EntityFileDto.newEntityFile();

			BeanUtils.copyProperties(uploadEntityFile, saveEntityFile);

			saveEntityFile.setName(multipartFile.getName());

			saveEntityFile.setOriginalFilename(multipartFile.getOriginalFilename());

			saveEntityFile.setExtension(StringUtils.lowerCase(FilenameUtils.getExtension(saveEntityFile.getOriginalFilename())));

			saveEntityFile.setFilename(UUID.randomUUID().toString() + Characters.dot.stringValue() + saveEntityFile.getExtension());

			EntityFileUtils.saveFile(multipartFile.getInputStream(), saveEntityFile.getFilename());

			entityFileList.add(ContextHolder.getBean(EntityFileService.class).saveByIdOrCode(EntityFileDto.class, saveEntityFile));

			EntityFileUtils.buildThumbnails(entityFileList, saveEntityFile, multipartFile);
		}

		return entityFileList;
	}

	/**
	 * 写入文件
	 *
	 * @param toFile
	 * @param filename
	 * @throws IOException
	 */
	public static void writeTo(final File toFile, final String filename) throws IOException {
		final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

		if (gridFSDBFile == null) {
			return;
		}

		gridFSDBFile.writeTo(toFile);
	}

	@Setter
	private static Map<String, Run2Action<HttpServletResponse, String>> writeToMap;

	static {
		writeToMap = Maps.newHashMap();

		writeToMap.put("xls,xlsx,doc,docx,ppt,pptx", new Run2Action<HttpServletResponse, String>() {
			@Override
			public void run(HttpServletResponse response, String filename) {
				response.setContentType("application/x-msdownload");

				try {
					response.setHeader("content-disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					throw new RuntimeException(e);
				}
			}
		});
	}

	/**
	 * 写入流
	 *
	 * @param response
	 * @param filename
	 *          文件名
	 * @throws IOException
	 */
	public static void writeTo(final HttpServletResponse response, final String filename) throws IOException {
		final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

		if (gridFSDBFile == null) {
			return;
		}

		response.setDateHeader("Expires", 0);
		response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
		response.addHeader("Cache-Control", "post-check=0, pre-check=0");
		response.setHeader("Pragma", "no-cache");

		String fileExtension = FilenameUtils.getExtension(filename);

		for (String writeToKey : writeToMap.keySet()) {
			if (StringUtils.containsIgnoreCase(writeToKey, fileExtension)) {
				writeToMap.get(writeToKey).run(response, filename);

				gridFSDBFile.writeTo(response.getOutputStream());

				return;
			}
		}

		response.setContentType(gridFSDBFile.getContentType());

		gridFSDBFile.writeTo(response.getOutputStream());
	}

	/**
	 * 写入文件
	 *
	 * @param toOutputStream
	 * @param filename
	 * @throws IOException
	 */
	public static void writeTo(final OutputStream toOutputStream, final String filename) throws IOException {
		final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

		if (gridFSDBFile == null) {
			return;
		}

		gridFSDBFile.writeTo(toOutputStream);
	}

	/**
	 * 写入流
	 *
	 * @param toFilename
	 * @param filename
	 * @throws IOException
	 */
	public static void writeTo(final String toFilename, final String filename) throws IOException {
		final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

		if (gridFSDBFile == null) {
			return;
		}

		gridFSDBFile.writeTo(toFilename);
	}
}
